package com.sh.data.engine.infrastructure.storage;

import com.sh.data.engine.infrastructure.config.MinioConfiguration;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import io.minio.messages.SseConfiguration;
import jakarta.annotation.PostConstruct;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author: mengzheng.mhc
 * @date: 2024/6/11 12:36
 */
@Slf4j
@Service
@ConditionalOnProperty(value = "data.engine.minio.enable", havingValue = "true")
public class MinioClientServiceImpl implements MinioClientService {

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private MinioConfiguration minioConfiguration;

    @PostConstruct
    private void init() {
        if (null == minioClient) {
            throw new RuntimeException("minio client didn't init,please check minio config");
        }
    }

    @SneakyThrows
    @Override
    public void createBucket(String bucketName) {
        minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
    }

    @SneakyThrows
    @Override
    public void setBucketPolicy(String bucketName, String policy) {
        minioClient.setBucketPolicy(
            SetBucketPolicyArgs.builder().bucket(bucketName).config(policy).build());
    }

    @SneakyThrows
    @Override
    public void setBucketEncryption(String bucketName) {
        SseConfiguration config = SseConfiguration.newConfigWithSseS3Rule();
        minioClient.setBucketEncryption(
            SetBucketEncryptionArgs.builder().bucket(bucketName).config(config).build());
    }

    @SneakyThrows
    @Override
    public List<Bucket> listBuckets() {
        return minioClient.listBuckets();
    }

    @SneakyThrows
    @Override
    public void removeBucket(String bucketName) {
        minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
    }

    @Override
    public void createFolder(String bucketName, String folder) {
        this.createFolder(bucketName, null, folder);
    }

    @SneakyThrows
    @Override
    public void createFolder(String bucketName, String parentFolder, String currentFolder) {
        String objectName =
            StringUtils.isBlank(parentFolder) ? currentFolder : parentFolder + "/" + currentFolder;
        if (!StringUtils.endsWith(objectName, "/")) {
            objectName = objectName + "/";
        }
        minioClient.putObject(
            PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(
                    new ByteArrayInputStream(new byte[]{}), 0, -1)
                .build());
    }

    @SneakyThrows
    @Override
    public void putObject(String bucketName, String folder, String fileName, InputStream stream) {
        String objectName = StringUtils.isBlank(folder) ? fileName : folder + "/" + fileName;
        minioClient.putObject(
            PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(
                    stream, -1, ObjectWriteArgs.MIN_MULTIPART_SIZE)
                .build());
    }

    @SneakyThrows
    @Override
    public String getFileUrl(
        String bucket, String object, Integer expireSecond, Boolean bucketPublic) {

        String endpoint = minioConfiguration.getEndpoint();

        String url;
        if (bucketPublic) {
            url =
                endpoint.endsWith("/")
                    ? (endpoint + bucket + "/" + object)
                    : (endpoint + "/" + bucket + "/" + object);

            return url;
        }

        GetPresignedObjectUrlArgs args =
            GetPresignedObjectUrlArgs.builder()
                .bucket(bucket)
                .object(object)
                .expiry(expireSecond, TimeUnit.SECONDS)
                .method(Method.GET)
                .build();

        url = minioClient.getPresignedObjectUrl(args);

        return url;
    }

    @SneakyThrows
    @Override
    public File downloadFile(String bucket, String object, String fileName) {
        String tempFilePath = FileUtils.getTempDirectoryPath() + "/" + fileName;

        DownloadObjectArgs args =
            DownloadObjectArgs.builder().bucket(bucket).object(object).filename(tempFilePath).build();
        minioClient.downloadObject(args);

        return new File(tempFilePath);
    }

    @SneakyThrows
    @Override
    public void removeFile(String bucket, String object) {
        RemoveObjectArgs args = RemoveObjectArgs.builder().bucket(bucket).object(object).build();
        minioClient.removeObject(args);
    }

    @SneakyThrows
    @Override
    public GetObjectResponse getObject(String bucket, String object) {
        GetObjectArgs args = GetObjectArgs.builder().bucket(bucket).object(object).build();
        return minioClient.getObject(args);
    }

    @SneakyThrows
    @Override
    public void composeObject(String bucket, String object, List<ComposeSource> sourceObjectList) {
        minioClient.composeObject(
            ComposeObjectArgs.builder()
                .bucket(bucket)
                .object(object)
                .sources(sourceObjectList)
                .build());
    }

    @SneakyThrows
    @Override
    public Boolean bucketExists(String bucket) {
        boolean b = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());
        return b;
    }

    @SneakyThrows
    @Override
    public Boolean isBucketEmpty(String bucket, boolean excludeEngineTempFolder) {
        if (!bucketExists(bucket)) {
            return true;
        }

        Iterable<Result<Item>> results =
            minioClient.listObjects(ListObjectsArgs.builder().bucket(bucket).build());

        Iterator<Result<Item>> iterator = results.iterator();

        boolean hasObject = false;
        while (iterator.hasNext()) {
            String objectName = iterator.next().get().objectName();
            if (excludeEngineTempFolder
                && StringUtils.equals(
                objectName, minioConfiguration.getTempFolderParent() + "/")) {
                continue;
            }
            hasObject = true;
            break;
        }

        return !hasObject;
    }
}
